#include <stdio.h>
#include <sys/epoll.h>
#include <fcntl.h>      //fcntl()
#include <stdlib.h>     //exit()
#include <sys/socket.h> //socket()
#include <sys/types.h>  //
#include <netinet/in.h> //struct sockaddr_in
#include <errno.h>      //perror()
#include <string.h>     //memset()
#include <unistd.h>     //close()
#include <arpa/inet.h>  //inet_addr

#define MAX_EVENTS 10
#define EXIT_FAILURE -1

void setnonblocking(int sock)
{
    int opts;
    opts = fcntl(sock, F_GETFL);
    if (opts < 0)
    {
        perror("fcntl(sock,GETFL)");
        exit(1);
    }
    opts = opts | O_NONBLOCK;
    if (fcntl(sock, F_SETFL, opts) < 0)
    {
        perror("fcntl(sock,SETFL,opts)");
        exit(1);
    }
}

void do_use_fd(int socket)
{
    size_t buflen = 32 * 1024;
    char *buffer[32 * 1024];
    long sizeRet = recv(socket, buffer, buflen, 0);
    int ierrno = errno;
    if (sizeRet == 0 || (sizeRet < 0 && (ierrno != EAGAIN && ierrno != EWOULDBLOCK && ierrno != EINTR)))
    {
        printf("sizeRet:%ld---ierrno:%d \n", sizeRet, ierrno);
        close(socket);
    }
    std::string revcData(buffer, sizeRet);
    printf("recvData:%s\n", revcData.c_str());
}

int main(int argn, char **argv)
{
    struct sockaddr addr;
    unsigned int addrlen = sizeof(addr);

    struct epoll_event ev, events[MAX_EVENTS];
    int listen_sock, conn_sock, nfds, epollfd;

    char *listenIp = "127.0.0.1";
    uint16_t listenPort = 50000;

    int domain = AF_INET;       //AF_INET IPv4 | AF_INET6 IPv6
    int type = SOCK_STREAM;     // TCP使用的Socket样式：SOCK_STREAM | UDP使用的socket样式：SOCK_DGRAM
    int protocol = IPPROTO_TCP; // IPPROTO_IP:0 默认协议 | IPPROTO_TCP TCP协议 | IPPROTO_UDP UDP协议 注意：协议类型需要与socket样式匹配
    listen_sock = socket(domain, type, protocol);
    if (listen_sock == -1)
    {
        printf("Can't create listen_sock socket\n");
        perror("perror：");
        return -1;
    }

    printf("create tcp listerner socket [%d]\n", listen_sock);

    printf("listerner socket [%d] start bind [%d]\n", listen_sock, listenPort);

    struct sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    // serverAddr.sin_addr.s_addr = inet_addr(listenIp);
    serverAddr.sin_port = htons(listenPort); //监听50000端口
    serverAddr.sin_family = AF_INET;         //IPv4

    //int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    int bindRet = bind(listen_sock, (sockaddr *)&serverAddr, sizeof(serverAddr));
    if (bindRet == -1)
    {
        printf("listerner socket [%d] Can't bind listen_sock socket\n", listen_sock);
        perror("perror：");
        close(listen_sock);
        return -1;
    }

    printf("listerner socket [%d] start listen_sock[%d]\n", listen_sock, listenPort);

    //int listen(int socket, int backlog);
    int backlog = 1;
    int listenRet = listen(listen_sock, 128); // 只接入一个连接
    if (listenRet == -1)
    {
        printf("listerner socket [%d] listen_sock socket error\n", listen_sock);
        perror("perror：");
        close(listen_sock);
        return -1;
    }

    printf("listerner socket [%d] succeed listen_sock[%d]\n", listen_sock, listenPort);

    epollfd = epoll_create1(0);
    if (epollfd == -1)
    {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1)
    {
        perror("epoll_ctl: listen_sock");
        exit(EXIT_FAILURE);
    }

    for (;;)
    {
        nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (nfds == -1)
        {
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }

        for (int n = 0; n < nfds; ++n)
        {
            if (events[n].data.fd == listen_sock)
            {
                conn_sock = accept(listen_sock,
                                   (struct sockaddr *)&addr, &addrlen);
                if (conn_sock == -1)
                {
                    perror("accept");
                    exit(EXIT_FAILURE);
                }
                setnonblocking(conn_sock);
                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = conn_sock;
                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                              &ev) == -1)
                {
                    perror("epoll_ctl: conn_sock");
                    exit(EXIT_FAILURE);
                }
            }
            else
            {
                printf("do_use_fd(%d)\n", events[n].data.fd);
                do_use_fd(events[n].data.fd);
            }
        }
    }

    printf("Hello World!\n");
    return 0;
}